package com.buaa.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.buaa.dto.UserDTO;
import com.buaa.entity.User;
import com.buaa.enums.ResponseCode;
import com.buaa.exception.ServiceException;
import com.buaa.mapper.UserMapper;
import com.buaa.secure.SecureUtil;
import com.buaa.secure.TokenUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User>  implements IService<User> {


    public List<User> getUserList(String policeId, String userName) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if (StrUtil.isNotBlank(userName) && StrUtil.isNotBlank(policeId)) {
            queryWrapper.like("user_name", userName);
            queryWrapper.like("police_id", policeId);
        } else {
            if (StrUtil.isNotBlank(userName))
                queryWrapper.like("user_name", userName);
            if (StrUtil.isNotBlank(policeId))
                queryWrapper.like("police_id", policeId);
        }
        try{
            return list(queryWrapper);
        }catch (ServiceException e){
            new ServiceException(ResponseCode.DB_EXEC_ERROR.getKey(), ResponseCode.DB_EXEC_ERROR.getDesc());
        }
        return null;
    }
    public User getOperateUser(HttpServletRequest request) {
        String token = request.getHeader("token");
        // 执行认证
        if (StrUtil.isBlank(token)) {
            throw new ServiceException(ResponseCode.LOGIN_TOKEN_NULL.getKey(), ResponseCode.LOGIN_TOKEN_NULL.getDesc());
        }
        // 获取 token 中的 userId
        String userId;
        try {
            // 解码JWT令牌，获取其中的信息
            userId = JWT.decode(token).getAudience().get(0);
//            // 从解码后的JWT中获取受众字段，即该令牌的接收者
//            List<String> result = decodedJWT.getAudience();
//            // 假设第一个受众代表用户ID，将其赋值给userId变量
//            userId = result.get(0);

        } catch (JWTDecodeException j) {
            throw new ServiceException(ResponseCode.LOGIN_TOKEN_FAILED.getKey(), ResponseCode.LOGIN_TOKEN_FAILED.getDesc());
        }
        // 根据token中的userid查询数据库
        User user = getUserByPoliceId(userId);
        if (user == null) {
            throw new ServiceException(ResponseCode.LOGIN_USER_NOT_EXIST.getKey(), ResponseCode.LOGIN_USER_NOT_EXIST.getDesc());
        }
        // 脱敏
        user.setUserPassword("");
        user.setSalt("");
        return user;
    }
    @Autowired
    private UserActivityServiceImpl userActivityService;

    public void logout(UserDTO userDTO) {

        QueryWrapper<User> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("police_id",userDTO.getPoliceId());
        User user = null;
        try{
            user = getOne(queryWrapper);
        }catch (Exception e){
            e.printStackTrace();
        }
        userActivityService.saveLoginoutActivity(user);
    }

    public UserDTO login(UserDTO userDTO) {
        QueryWrapper<User> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("police_id",userDTO.getPoliceId());
        User one;
        try{
            one = getOne(queryWrapper);
            if (one == null) {
                throw new ServiceException(ResponseCode.COMMON_SYS_ERROR.getKey(), ResponseCode.LOGIN_USER_NOT_EXIST.getDesc());
            }
        }catch (Exception e){
            System.out.println("e.getMessage() = " + e.getMessage());
            throw new ServiceException(ResponseCode.COMMON_SYS_ERROR.getKey(), ResponseCode.COMMON_SYS_ERROR.getDesc());
        }
        String password = userDTO.getUserPassword();
        password = SecureUtil.sha256(password, one.getSalt());
        if (password.equals(one.getUserPassword())) {
            BeanUtil.copyProperties(one, userDTO, true);
            /**
             * 生成用户令牌
             * 使用PoliceId和UserPassword生成令牌，确保用户身份的安全性和唯一性。
             * 令牌用于后续的用户身份验证，提高系统安全性。
             */
            String token = TokenUtils.genToken(one.getPoliceId(), one.getUserPassword());
            /**
             * 将生成的令牌保存到用户数据对象中
             * 令牌将被用于后续的用户身份验证，确保用户操作的安全性。
             */
            userDTO.setToken(token);
            userDTO.setUserPassword("");
        } else {
            throw new ServiceException(ResponseCode.LOGIN_USER_PASSWORD_ERROR.getKey(), ResponseCode.LOGIN_USER_PASSWORD_ERROR.getDesc());
        }
        // 登录成功，记录log
        try{
            //  没有返回值，是因为记录成功或者失败，不要影响用户登录的结果，异常也不抛出
            userActivityService.saveLoginActivity (one);
        }catch (Exception e){
            e.printStackTrace();//这里假设查询了多于1条记录，就让他报系统错误
        }
        return userDTO;
    }


    public long countTotal(){
          return this.count();
    }

    /**
     * 检查用户名是否已存在。
     *
     * @param userName 待检查的用户名
     * @return 如果用户名已存在，则返回true；否则返回false。
     */
    public boolean checkUserName(String userName){
        // 创建查询条件，指定查询用户名为指定值的用户
        Wrapper wrapper = new QueryWrapper<User>().eq("user_name", userName);
        // 根据查询条件统计用户数量
        long count = this.count(wrapper);
        // 如果统计结果大于0，说明用户名已存在，返回true；否则返回false
        return count > 0 ? true : false;
    }
    /**
     * 检查警察身份证号是否存在于系统中。
     *
     * @param policeId 警察的身份证号码
     * @return 如果身份证号存在，则返回true；否则返回false。
     */
    public boolean checkPoliceId(String policeId){
        // 创建查询条件，指定查询警察身份证号为policeId的用户
        Wrapper wrapper = new QueryWrapper<User>().eq("police_id", policeId);
        // 查询符合查询条件的用户数量
        long count = this.count(wrapper);
        // 如果查询结果数量大于0，说明身份证号存在，返回true；否则返回false
        return count > 0 ? true : false;
    }

    public User getUserByPoliceId(String policeId){
        // 创建查询条件，指定查询警察身份证号为policeId的用户
        Wrapper wrapper = new QueryWrapper<User>().eq("police_id", policeId);
        // 查询符合查询条件的用户数量
         return getOne(wrapper);
    }
    /**
     * 根据用户ID获取用户盐值。
     *
     * @param id 用户的唯一标识符。
     * @return User 对象，包含指定ID用户的盐值信息。
     */
    public User getSaltbyId(Integer id){
        return getById(id);
    }
    public boolean deleteById(Integer id){
        return removeById(id);
    }

    public boolean updateById(User user){
        return super.updateById(user);
    }
    public Map<String,Object>  findPage(String policeId, String userName, Integer pageNum, Integer pageSize){
        // 创建一个分页对象，指定页码和每页大小
        IPage<User> page = new Page<>(pageNum, pageSize);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if ((null != userName && "" != userName)  && ( null != policeId && "" != policeId )) {
            queryWrapper.like("user_name", userName);
            queryWrapper.like("police_id", policeId);
        }
        else {
            if (null != userName && "" != userName)
                queryWrapper.like("user_name", userName);
            if (null != policeId && "" != policeId)
                queryWrapper.like("police_id", policeId);
        }
        long total = count(queryWrapper);
        queryWrapper.orderByDesc("create_time")
                .last("LIMIT " + pageNum + "," + pageSize);
        //        // 调用page方法，根据分页对象查询用户信息
        IPage<User> userPage = page(page, queryWrapper); // 调用 page 方法
        List<User> userList = userPage.getRecords();
        Map<String,Object> res=new HashMap<>();
        res.put("pagedata",userList);
        res.put("total",total);
        return res;
        // 返回查询结果中的用户记录列表

    }
    public Map<String,Object>  findPage3(String userName, Integer pageNum, Integer pageSize){
        // 创建一个分页对象，指定页码和每页大小
        IPage<User> page = new Page<>(pageNum, pageSize);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("user_name", userName);
        long total = count(queryWrapper);
        queryWrapper.orderByDesc("create_time")
                .last("LIMIT " + pageNum + "," + pageSize);
        //        // 调用page方法，根据分页对象查询用户信息
        IPage<User> userPage = page(page, queryWrapper); // 调用 page 方法
        List<User> userList = userPage.getRecords();
        Map<String,Object> res=new HashMap<>();
        res.put("pagedata",userList);
        res.put("total",total);
        return res;
        // 返回查询结果中的用户记录列表

    }

    /**
     * 分页查询用户信息。
     *
     * @param pageNum 页码，表示需要查询的页数。
     * @param pageSize 每页显示的数量，控制每页返回的用户数量。
     * @return 返回一个用户列表，这个列表是根据传入的页码和每页数量分页后的结果。
     */
    public Map<String,Object>  findPage2(Integer pageNum, Integer pageSize){
        // 创建一个分页对象，指定页码和每页大小
        IPage<User> page = new Page<>(pageNum, pageSize);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("create_time")
                .last("LIMIT " + pageNum + "," + pageSize);

        //        // 调用page方法，根据分页对象查询用户信息
        IPage<User> userPage = page(page, queryWrapper); // 调用 page 方法
        List<User> userList = userPage.getRecords();
        Map<String,Object> res=new HashMap<>();
        res.put("pagedata",userList);
        res.put("total",countTotal());
        return res;
        // 返回查询结果中的用户记录列表

    }

    public boolean saveOrUpdate2(User user, User currentOpUser){
        if (null == user)
            return false;
        else if (null == user.getId()) {
            // insert
            if (checkPoliceId(user.getPoliceId()))
                throw new ServiceException(ResponseCode.USER_ALREADY_EXIST.getKey(), ResponseCode.USER_ALREADY_EXIST.getDesc());
            user.setCreateTime(LocalDateTime.now());
            user.setCreateBy(currentOpUser.getUserName()); //设置操作人
            // 否则就是创建用户
            // 对密码进行 加密 + 加盐
            String userPassword = user.getUserPassword();
            String salt = SecureUtil.generateSalt();
            String encodePwd = SecureUtil.sha256(userPassword, salt);
            user.setUserPassword(encodePwd);
            user.setSalt(salt);
            boolean result = false;
            try{
                result = save(user);
            }catch(Exception exception){
                throw new ServiceException(ResponseCode.COMMON_SYS_ERROR.getKey(), ResponseCode.COMMON_SYS_ERROR.getDesc());
            }
            return result;

        } else { // 说明这个用户已经存在, 设置操作时间和操作人
            user.setRevisionTime(LocalDateTime.now());
            user.setReviser(currentOpUser.getUserName()); //设置操作人
            String userPassword = user.getUserPassword();
            // 存在没有 salt的情况
            String salt = getSaltbyId(user.getId()).getSalt();
            //如果没有salt，则新建一个salt
            if (null == salt) {
                salt = SecureUtil.generateSalt();
                user.setSalt(salt);
            }
            // 有可能更新密码，所以对密码再次encode
            String encodePwd = SecureUtil.sha256(userPassword, salt);
            user.setUserPassword(encodePwd);
            boolean result = false;
            try{
                result = updateById(user);
            }catch(Exception exception){
                throw new ServiceException(ResponseCode.COMMON_SYS_ERROR.getKey(), ResponseCode.COMMON_SYS_ERROR.getDesc());
            }
            return result;
        }

    }
    public boolean saveOrUpdate(User user){
        User obj = null;
        if ( null != user.getId() ) {
            obj = this.getById(user.getId());
            if (null != obj) { // 说明这个用户已经存在, 设置操作时间和操作人
                user.setRevisionTime(LocalDateTime.now());
                user.setReviser("999"); //设置操作人
            }
        }
        // 否则就是创建用户
        // 对密码进行 加密 + 加盐
        String userPassword = user.getUserPassword();
        String encodePwd = SecureUtil.sha256(userPassword, SecureUtil.generateSalt());
        user.setUserPassword(encodePwd);
        return super.saveOrUpdate(user);
    }

}
